#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
配置处理器
处理系统配置加载、验证和环境变量覆盖
"""

import os
import json
import logging
from typing import Dict, Any, Optional
from dotenv import load_dotenv

# 设置日志
logger = logging.getLogger(__name__)

class ConfigHandler:
    """
    配置处理器，负责加载、验证和管理配置
    """
    
    def __init__(self, config_path: str = None, env_file: str = None):
        """
        初始化配置处理器
        
        Args:
            config_path: 配置文件路径
            env_file: 环境变量文件路径(.env)
        """
        # 加载环境变量
        if env_file and os.path.exists(env_file):
            load_dotenv(env_file)
        else:
            # 尝试查找默认的.env文件
            default_env_files = ['.env', '.env.local']
            for env_file in default_env_files:
                if os.path.exists(env_file):
                    load_dotenv(env_file)
                    break
        
        # 优先使用传入的配置文件
        self.config_path = config_path
        
        # 如果未指定配置路径，查找可能的配置文件
        if not self.config_path:
            possible_configs = [
                'config.local.json',  # 本地开发配置
                'config.json',        # 默认配置
            ]
            
            for config in possible_configs:
                # 尝试在当前目录查找
                if os.path.exists(config):
                    self.config_path = config
                    break
                
                # 尝试在模块目录查找
                module_config = os.path.join(
                    os.path.dirname(os.path.abspath(__file__)), 
                    config
                )
                if os.path.exists(module_config):
                    self.config_path = module_config
                    break
        
        # 加载配置
        self.config = self._load_config()
        
        # 使用环境变量覆盖配置
        self._override_with_env_vars()
        
        # 验证配置
        self._validate_config()
    
    def _load_config(self) -> Dict[str, Any]:
        """
        加载配置文件
        
        Returns:
            Dict: 配置信息
        """
        default_config = {
            'login': {
                'login_url': 'http://wspj.gxmu.edu.cn/login.jsp',
                'username': '',
                'password': '',
                'timeout': 30,
                'retry_count': 3
            },
            'probabilities': {
                "teaching_quality": {
                    "satisfied": 0.7,
                    "neutral": 0.2,
                    "unsatisfied": 0.1
                }
            },
            'screenshot_dir': 'screenshots',
            'browser': {
                'headless': False
            }
        }
        
        if not self.config_path or not os.path.exists(self.config_path):
            logger.warning(f"配置文件不存在或未指定: {self.config_path}, 使用默认配置")
            return default_config
        
        try:
            with open(self.config_path, 'r', encoding='utf-8') as f:
                config = json.load(f)
            logger.info(f"成功加载配置文件: {self.config_path}")
            return config
        except Exception as e:
            logger.error(f"加载配置文件失败: {e}, 使用默认配置")
            return default_config
    
    def _override_with_env_vars(self):
        """
        使用环境变量覆盖配置项
        环境变量命名规则: EVAL_<SECTION>_<KEY>
        例如: EVAL_LOGIN_USERNAME 会覆盖 config['login']['username']
        """
        env_prefix = 'EVAL_'
        
        # 处理登录信息
        login_prefix = f"{env_prefix}LOGIN_"
        for key in ['username', 'password', 'login_url', 'timeout', 'retry_count']:
            env_var = f"{login_prefix}{key.upper()}"
            if env_var in os.environ:
                # 对于数字类型的配置，进行类型转换
                if key in ['timeout', 'retry_count']:
                    try:
                        self.config['login'][key] = int(os.environ[env_var])
                    except ValueError:
                        logger.warning(f"环境变量 {env_var} 不是有效的整数，使用配置文件值")
                else:
                    self.config['login'][key] = os.environ[env_var]
                logger.info(f"已使用环境变量 {env_var} 覆盖配置")
        
        # 处理浏览器配置
        browser_prefix = f"{env_prefix}BROWSER_"
        for key in ['headless']:
            env_var = f"{browser_prefix}{key.upper()}"
            if env_var in os.environ:
                # 处理布尔类型
                if key == 'headless':
                    value = os.environ[env_var].lower()
                    if value in ['true', '1', 'yes', 'y']:
                        self.config['browser'][key] = True
                    elif value in ['false', '0', 'no', 'n']:
                        self.config['browser'][key] = False
                    logger.info(f"已使用环境变量 {env_var} 覆盖配置")
        
        # 处理截图目录
        if f"{env_prefix}SCREENSHOT_DIR" in os.environ:
            self.config['screenshot_dir'] = os.environ[f"{env_prefix}SCREENSHOT_DIR"]
            logger.info(f"已使用环境变量 {env_prefix}SCREENSHOT_DIR 覆盖配置")
    
    def _validate_config(self) -> bool:
        """
        验证配置是否有效
        
        Returns:
            bool: 配置是否有效
        """
        is_valid = True
        
        # 检查必要的配置部分是否存在
        required_sections = ['login', 'probabilities']
        for section in required_sections:
            if section not in self.config:
                logger.error(f"缺少必要的配置部分: {section}")
                self.config[section] = {}
                is_valid = False
        
        # 检查登录配置
        login = self.config.get('login', {})
        required_login_fields = ['login_url', 'username', 'password']
        for field in required_login_fields:
            if field not in login or not login[field]:
                # 用户名和密码可以通过环境变量或命令行提供，所以这里只发出警告
                logger.warning(f"登录配置中缺少字段: {field}")
                if field not in login:
                    login[field] = ''
        
        # 检查概率配置
        probabilities = self.config.get('probabilities', {})
        if not probabilities:
            logger.warning("缺少评价概率配置，将使用默认值")
            self.config['probabilities'] = {
                "teaching_quality": {
                    "satisfied": 0.7,
                    "neutral": 0.2,
                    "unsatisfied": 0.1
                }
            }
        
        # 确保每个概率配置的总和接近1.0
        for category, probs in probabilities.items():
            total = sum(probs.values())
            if abs(total - 1.0) > 0.01:  # 允许1%的误差
                logger.warning(f"概率配置 '{category}' 的总和不为1.0，实际值: {total}")
                # 自动调整
                factor = 1.0 / total
                for key in probs:
                    probs[key] *= factor
                logger.info(f"已自动调整 '{category}' 概率以使总和为1.0")
        
        return is_valid
    
    def get_config(self) -> Dict[str, Any]:
        """
        获取完整配置
        
        Returns:
            Dict: 配置信息
        """
        return self.config
    
    def get_section(self, section: str) -> Dict[str, Any]:
        """
        获取指定部分的配置
        
        Args:
            section: 配置部分名称
            
        Returns:
            Dict: 部分配置信息
        """
        return self.config.get(section, {})
    
    def get_value(self, section: str, key: str, default: Any = None) -> Any:
        """
        获取指定配置值
        
        Args:
            section: 配置部分名称
            key: 配置键名
            default: 默认值
            
        Returns:
            Any: 配置值
        """
        section_data = self.config.get(section, {})
        return section_data.get(key, default)
    
    def update_config(self, section: str, key: str, value: Any):
        """
        更新配置值
        
        Args:
            section: 配置部分名称
            key: 配置键名
            value: 新的配置值
        """
        if section not in self.config:
            self.config[section] = {}
        
        self.config[section][key] = value
    
    def save_config(self, path: str = None) -> bool:
        """
        保存配置到文件
        
        Args:
            path: 保存路径，不提供则使用加载时的路径
            
        Returns:
            bool: 是否成功保存
        """
        save_path = path or self.config_path
        if not save_path:
            logger.error("未指定保存路径，无法保存配置")
            return False
        
        try:
            with open(save_path, 'w', encoding='utf-8') as f:
                json.dump(self.config, f, ensure_ascii=False, indent=2)
            logger.info(f"配置已保存到: {save_path}")
            return True
        except Exception as e:
            logger.error(f"保存配置失败: {e}")
            return False

# 创建一个默认配置处理器实例，方便其他模块导入使用
default_config_handler = ConfigHandler()

def get_config() -> Dict[str, Any]:
    """
    获取默认配置
    
    Returns:
        Dict: 配置信息
    """
    return default_config_handler.get_config()

# 用于CLI工具的配置检查函数
def validate_config_cli(config_path: str) -> bool:
    """
    验证指定路径的配置文件
    
    Args:
        config_path: 配置文件路径
        
    Returns:
        bool: 配置是否有效
    """
    handler = ConfigHandler(config_path)
    config = handler.get_config()
    
    print("\n配置验证结果:")
    print("=" * 50)
    
    # 检查登录配置
    login = config.get('login', {})
    print("\n[登录配置]")
    for key, value in login.items():
        # 不显示密码明文
        if key == 'password' and value:
            print(f"  {key}: ****** (已设置)")
        else:
            print(f"  {key}: {value}")
    
    # 检查概率配置
    probabilities = config.get('probabilities', {})
    print("\n[评价概率配置]")
    for category, probs in probabilities.items():
        print(f"  {category}:")
        total = sum(probs.values())
        for option, prob in probs.items():
            print(f"    {option}: {prob:.2f}")
        print(f"    总和: {total:.2f}")
    
    # 检查浏览器配置
    browser = config.get('browser', {})
    print("\n[浏览器配置]")
    for key, value in browser.items():
        print(f"  {key}: {value}")
    
    print("\n[其他配置]")
    print(f"  截图目录: {config.get('screenshot_dir', 'screenshots')}")
    
    print("\n配置文件路径:", config_path)
    print("=" * 50)
    
    return True

# 命令行入口
if __name__ == "__main__":
    import argparse
    
    parser = argparse.ArgumentParser(description="配置文件验证工具")
    parser.add_argument("--config", help="配置文件路径")
    parser.add_argument("--validate", action="store_true", help="验证配置文件")
    parser.add_argument("--upload", action="store_true", help="上传到Gitee仓库")
    parser.add_argument("--repo", default="https://gitee.com/CRGOGOGO/edu-eval-pro.git", help="Gitee仓库地址")
    
    args = parser.parse_args()
    
    if args.validate:
        validate_config_cli(args.config) 